#!/usr/bin/env python
# 
"""
SQl table row class factory.

$Id$
"""
__revision__ = "$Id$"

import baseSQLtypes
from sql import sql_quote
#-----------------------------------------------------------

def ROW (tname,sequence,schema='public',setdefault=1,strict=0) :
    """
    Creates class - table row.

    tname - table name
    sequence - base types of fields.
    """
    slots = tuple([c.__name__ for c in sequence])

    class row(object) :
        table_name = tname
        fields = sequence
        __slots__ = slots
        #---------------------------------------------------
        
        def __init__ ( self , dict_=None ) :
            if self.__class__.strict :
                assert len(self.__class__.fields)==len(dict_)
            for cls in self.__class__.fields :
                name = cls.__name__
                sname = cls.sqlname()
                if self.__class__.strict : value = dict_[sname]
                elif dict_ : value = dict_.get(sname)
                else : value = None
                if value is None and self.__class__.setdefault :
                    value = cls.default()
                if value is None : setattr(self,name,None)
                else : setattr(self,name,cls(value))
        #---------------------------------------------------

        def field_names ( cls ) :
            return ','.join([c.sqlname() for c in cls.fields])
        field_names = classmethod ( field_names )
        #---------------------------------------------------

        def valid ( cls , dict_ ) :
            result = {}
            for class_ in cls.fields :
                name = class_.__name__
                sname = class_.sqlname()
                if dict_ : value = dict_.get(sname)
                else : value = None
                if value is None and cls.setdefault :
                    value = class_.default()
                if value is not None and class_.isValid(value) :
                    result[sname] = class_(value)
            return result
                
        valid = classmethod(valid)
        #---------------------------------------------------
        def check ( cls , dict_ ) :
            if cls.strict : assert len(cls.fields)==len(dict_)
            result = {}
            for class_ in cls.fields :
                name = class_.__name__
                sname = class_.sqlname()
                if cls.strict : value = dict_[sname]
                elif dict_ : value = dict_.get(sname)
                else : value = None
                if value is None and cls.setdefault :
                    value = class_.default()
                if value is not None : result[sname] = class_.check(value)
            return result
                
        check = classmethod(check)
        #---------------------------------------------------

        def save ( self , cursor ) :
            k = ','.join([p.sqlname() for p in self.__class__.fields])
            v = []
            for p in self.__class__.fields :
                a = getattr(self,p.__name__)
                if a is None : v.append('NULL')
                else : v.append(a.sql())
            v = ','.join(v)
            cursor.execute (
                "insert into %s.%s (%s)\nvalues (%s)" %
                # don't mix with field names! (__class__ used)
                (self.__class__.schema,self.__class__.table_name,k,v) )
        #---------------------------------------------------

        def dump ( self ) :
            for p in self.__class__.fields : print getattr(self,p.__name__),
            print
        #---------------------------------------------------

        def copy_from_line ( self , delimiter='\t' ) :
            return delimiter.join([
                getattr(self,f.__name__).sql_from(delimiter)
                for f in self.__class__.fields
                ])+'\n'
        #---------------------------------------------------

        def dump_schema ( cls ) :
            print 'CREATE TABLE %s (' % cls.table_name
            print ' ,\n'.join(['\t'+c.sqlname()+' \t'+c.sql_type
                               for c in cls.fields])
            print '\t) ;'
        dump_schema = classmethod(dump_schema) 
        #---------------------------------------------------
    row.schema = schema
    row.strict = strict
    row.setdefault = setdefault
    return row
#-----------------------------------------------------------

def auto_row ( table_name , conn , schema , setdefault=1 , strict=0 ) :
    cr = conn.cursor()
    cr.execute ( """
    select column_name , data_type , character_maximum_length ,
    numeric_precision , numeric_scale , numeric_precision_radix ,
    ordinal_position
    from information_schema.columns
    where table_schema = '%s' and table_name='%s' -- or table_name='tmptmp'
    order by ordinal_position
    """ % (sql_quote(schema),sql_quote(table_name)) )

    class_list = []
    for r in cr.fetchall() :
        ( column_name , data_type , character_maximum_length ,
          numeric_precision , numeric_scale , numeric_precision_radix ,
          ordinal_position ) = r
        #print '****' , r
        if data_type=='character varying' and character_maximum_length is None :
            data_type = 'text'

        if data_type == 'integer' :
            class tmp(baseSQLtypes.INTEGER) : sql_name=column_name
            class_list.append ( tmp )
        elif data_type == 'numeric' :
            class tmp(baseSQLtypes.NUMERIC(numeric_precision,numeric_scale)) :
                sql_name=column_name
            class_list.append ( tmp )
        elif data_type == 'text' :
            class tmp(baseSQLtypes.TEXT) : sql_name=column_name
            class_list.append ( tmp )
        elif data_type == 'character varying' :
            class tmp(baseSQLtypes.VARCHAR(character_maximum_length)) :
                sql_name=column_name
            class_list.append ( tmp )
        elif data_type == 'smallint' :
            class tmp(baseSQLtypes.SMALLINT) : sql_name=column_name
            class_list.append ( tmp )
        elif data_type == 'boolean' :
            class tmp(baseSQLtypes.BOOLEAN) : sql_name=column_name
            class_list.append ( tmp )
        elif data_type == 'bigint' :
            class tmp(baseSQLtypes.BIGINT) : sql_name=column_name
            class_list.append ( tmp )
        elif data_type == 'inet' :
            class tmp(baseSQLtypes.INET) : sql_name=column_name
            class_list.append ( tmp )
        elif data_type == 'timestamp without time zone' :
            class tmp(baseSQLtypes.TIMESTAMP) : sql_name=column_name
            class_list.append ( tmp )
        elif data_type == 'date' :
            class tmp(baseSQLtypes.DATE) : sql_name=column_name
            class_list.append ( tmp )
        else : raise TableAttrError ( 'Unimplemented column type: '+str(r) )
    for i,c in enumerate(class_list) : c.__name__ = 'field%d' % i
    if not class_list :
        raise TypeError , 'No such table: %s.%s' % (schema,table_name) 
    return ROW(table_name,class_list,schema,setdefault,strict)
#===========================================================
